
package com.insigma.business.common.controller;

import cn.hutool.core.util.ReUtil;
import com.google.common.cache.Cache;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import com.insigma.business.qrcode.common.dto.ApiConstant;
import com.insigma.business.qrcode.common.dto.ErrorCodeEnum;
import com.insigma.business.qrcode.common.dto.PermissionResourceDTO;
import com.insigma.business.qrcode.common.dto.PermissionRoleDTO;
import com.insigma.business.qrcode.common._consumer.CommonConsumer;
import com.insigma.business.qrcode.common.template.BasePagingResponse;
import com.insigma.business.qrcode.common.template.CacheManager;
import com.insigma.business.qrcode.common.template.MemCacheManager;
import com.insigma.business.qrcode.common.template.area.ServiceTemplate;
import com.insigma.business.qrcode.common.util.*;
import com.insigma.business.qrcode.common.vo.*;
import com.insigma.business.qrcode.soldier.dto.RequestObjFO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Slf4j
public class BaseGatewayController {

    @Autowired
    protected ServiceTemplate serviceTemplate;
    @Autowired
    protected CacheManager cacheManager;

    @Autowired
    private CommonConsumer commonConsumer;

    private Set<String> noAuthApi = Sets.newHashSet("gov.ss.privilege.security.mobilePwdLogin",
            "gov.ss.privilege.security.smsLogin",
            "gov.ss.privilege.security.csdn",
            "gov.ss.privilege.security.szhz.login",
            "gov.ss.privilege.security.zzd.login",
            "gov.ss.privilege.security.pwd.retrieve",
            "common.ss.privilege.security.register",
            "common.ss.privilege.security.accountPwdLogin",
            "ss.common.base.log.save",
            "gov.ss.privilege.security.ytxSendSms",
            "gov.ss.soldier.basic.info.downLoadInfo"
            );


    @SuppressWarnings("unchecked")
    public void setupResult(final BaseResponse remoteResponse, final RestfulResult result) {
        if (remoteResponse.isSuccess()) {
            result.setCode(RestfulCodeEnum.SUCCESS.getCode());
            result.setMsg(RestfulCodeEnum.SUCCESS.getDesc());
            if (remoteResponse instanceof BasePagingResponse) {
                result.setData(buildPage((BasePagingResponse) remoteResponse));
            } else {
                result.setData(remoteResponse.getDto());
            }
        } else {
            if (StringUtils.equals(String.valueOf(HttpStatus.UNAUTHORIZED.value()), remoteResponse.getErrorCtx().getErrorCode())) {
                result.setCode(remoteResponse.getErrorCtx().getErrorCode());
            } else {
                result.setCode(RestfulCodeEnum.INTERNAL_ERROR.getCode());
            }

            if (remoteResponse.getErrorCtx().getErrorMsg() != null && remoteResponse.getErrorCtx().getErrorMsg().length() > 500) {
                ExceptionUtil.error(log, remoteResponse.getErrorCtx().getErrorMsg());
                result.setMsg("系统出错，请联系客服");
            } else {
                result.setMsg(remoteResponse.getErrorCtx().getErrorMsg());
            }
            if (remoteResponse instanceof BasePagingResponse) {
                result.setData(buildPage((BasePagingResponse) remoteResponse));
            }
        }
    }


    private Page buildPage(final BasePagingResponse<List<BaseVO>> remotePagingResponse) {
        final Page<BaseVO> page = new Page<>(remotePagingResponse.getPageNo(),
                remotePagingResponse.getPageSize(), remotePagingResponse.getTotalCount());
        page.setVoList(remotePagingResponse.getDto());
        page.setAllCount(remotePagingResponse.getAllCount());
        page.setExtra(remotePagingResponse.getExtra());
        page.setHeader(remotePagingResponse.getHead());
        return page;
    }


    protected SessionBasis buildSessionBasis(final HttpServletRequest request, final RequestObjFO requestObj, final String userType,
                                             final String visitor, final String traceId, final String spandId,
                                             final String frontUniqueSn) {
        final SessionBasis sessionBasis = new SessionBasis();
        sessionBasis.setUserType(userType);
        sessionBasis.setTerminalAppType(requestObj.getSource());
        sessionBasis.setTerminalIp(ClientUtil.getEndIp(request));
        sessionBasis.setVisitorId(visitor);
        sessionBasis.setTraceId(traceId);
        sessionBasis.setSpanId(spandId);
        sessionBasis.setLatestAccessDatetime(new Date());
        sessionBasis.setFrontUniqueSn(frontUniqueSn);
        sessionBasis.setSrcAppName("GW_MIX");
        sessionBasis.setDestAppName(requestObj.getOperation());

        return sessionBasis;
    }

    protected void checkPermission(RequestObjFO requestObj) {
        String api = parseApiServiceType(requestObj.getOperation());

        if (isNoAuthApi(api)) {
            return;
        }


        SessionGovEmployee sessionGovEmployee = RpcContextUtil.getSessionGovEmployee();
        if (sessionGovEmployee != null) {
            String roleCodes = sessionGovEmployee.getRoleCode();
            checkApiPermission(api, roleCodes);
        }
    }

    private Boolean isNoAuthApi(String api) {
        if (noAuthApi.contains(api)) {
            return true;
        }
        return false;
    }

    private void checkApiPermission(String api, String roleCodes) {
        if (Strings.isBlank(roleCodes)) {
            return;

        }

        for (String relCode : roleCodes.split(",")) {
            if (Strings.isNotBlank(relCode)) {
                relCode = relCode.trim();
                Boolean checkResult = getCachedCodePermissionMatch(api, relCode);
                if (checkResult) {
                    return;
                }
            }
        }
        throw new BizException(ErrorCodeEnum.ERROR_NO_AUTH);
    }


    private Boolean getCachedCodePermissionMatch(String api, String roleCode) {
        Cache<String, Object> cacheManager = MemCacheManager.getInstance(10L, TimeUnit.MINUTES);
        String key = "PERMISSION_CHECK_RESULT_" + api + "_" + roleCode;

        Object o = cacheManager.getIfPresent(key);


        if (o == null) {
            o = Boolean.TRUE;
            Map<String, List<PermissionResourceDTO>> roleResourceMap = getCachedRoleMap();
            List<PermissionResourceDTO> permissionResourceDTOS = roleResourceMap.get(roleCode);
            if (CollectionUtils.isNotEmpty(permissionResourceDTOS)) {
                Boolean combineResult = null;
                for (PermissionResourceDTO permissionResourceDTO : permissionResourceDTOS) {
                    Boolean apiPass = isApiPass(api, permissionResourceDTO);
                    if (Boolean.TRUE.equals(apiPass)) {
                        combineResult = true;
                        break;
                    }
                    if (Boolean.FALSE.equals(apiPass)) {
                        combineResult = false;
                    }
                }

                if (combineResult == null) {
                    combineResult = true;
                }

                o = combineResult;
            }
            cacheManager.put(key, o);
        }
        return (Boolean) o;
    }


    private Boolean isApiPass(String api, PermissionResourceDTO permissionResourceDTO) {
        if (Strings.isNotBlank(permissionResourceDTO.getBlacklist()) && Strings.isNotBlank(permissionResourceDTO.getWhitelist())) {
            return null;
        }

        boolean result = true;

        String blacklist = permissionResourceDTO.getBlacklist();
        if (Strings.isNotBlank(blacklist)) {
            Set<String> blackSet = cutToList(blacklist);
            Boolean checkResult = isApiMatch(api, blackSet);

            if (checkResult) {
                result = false;
            }
        }

        if (result) {
            return true;
        }

        String whitelist = permissionResourceDTO.getWhitelist();
        if (Strings.isNotBlank(whitelist)) {
            if (Strings.isNotBlank(whitelist)) {
                Set<String> whitelistSet = cutToList(whitelist);
                Boolean checkResult = isApiMatch(api, whitelistSet);
                if (checkResult) {
                    return true;
                }
            }
        }
        return result;
    }

    private Boolean isApiMatch(String api, Set<String> set) {
        for (String regex : set) {
            boolean match = ReUtil.isMatch(regex, api);
            if (match) {
                return true;
            }
        }
        return false;
    }

    private Set<String> cutToList(String str) {
        if (Strings.isBlank(str)) {
            return new HashSet<>();
        }
        Set<String> set = Sets.newHashSet();
        String[] split = str.split(",");
        for (String s : split) {
            if (Strings.isNotBlank(s)) {
                for (String item : s.split("\r?\n")) {
                    if (Strings.isNotBlank(item)) {
                        set.add(item.trim());
                    }
                }
            }
        }
        return set;
    }

    private Map<String, List<PermissionResourceDTO>> getCachedRoleMap() {
        Cache<String, Object> cacheManager = MemCacheManager.getInstance(10L, TimeUnit.MINUTES);
        String key = "ALL_ROLE_RESOURCE_MAP";
        Object o = cacheManager.getIfPresent(key);

        if (o == null) {
            List<PermissionRoleDTO> permissionRoleDTOS = commonConsumer.queryAllRoleDetail();
            Map<String, List<PermissionResourceDTO>> roleResourceMap = Maps.newHashMap();
            for (PermissionRoleDTO permissionRoleDTO : permissionRoleDTOS) {
                roleResourceMap.put(permissionRoleDTO.getCode(), permissionRoleDTO.getResources());
            }
            cacheManager.put(key, roleResourceMap);
            o = roleResourceMap;
        }
        return (Map<String, List<PermissionResourceDTO>>) o;
    }


    private String parseApiServiceType(final String operation) {
        return operation.substring(operation.indexOf(ApiConstant.SPLIT_CHAR_DOT) + 1);
    }

}
